 
  

 






<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 
<html>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do?Id=118 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:28:07 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
<head>
 <title>
  Java Practices -> Read-write locks
 </title>
 <link rel="stylesheet" type="text/css" href="../stylesheet8.css" media="all">
 
 <link rel="shortcut icon" href='../images/favicon.ico' type="image/vnd.microsoft.icon">
 <meta name="description" content="Concise presentations of java programming practices, tasks, and conventions, amply illustrated with syntax highlighted code examples.">
 
 <meta name='keywords' content='cache,JDBC,readwrite,rw,database,lock,java,java programming,java practices,java idiom,java style,java design patterns,java coding conventions,'>
 
 
</head>
 
<body>


<div class='menu-bar'>
 
  <a href='../home/HomeAction.html' title='Table of Contents'>Home</a> |
  <a href='../vote/VoteSummaryAction-2.html' title='View Poll Results'>Poll</a> |
   
  <A href='../feedback/FeedbackAction451f-2.html?Operation=Show' title='Send Your Feedback'>Wiki</a> |
  <b><a href='../source/SourceAction-2.html' title='Grab Source Code'>Source Code</a></b><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |

  <a href='http://www.web4j.com/Java_Web_Application_Framework_Overview.jsp?From=1' title='Free Download - Java Web Application Framework'><b>WEB4J</b></a> |
  
  <a href='http://www.date4j.net/' title='Replacement for java.util.Date'><b>DATE4J</b></a> |

   <a href='../references/ReferencesAction-2.html' title='References'>Links</a>
   
  <form action='http://www.javapractices.com/search/SearchAction.do' method='get' class='search-form'>
   <input type='text' name='SearchTerms' value="" size=12 maxlength=50 class='search'>
   <input type='submit' value="Search">
  </form>
 
</div>

<P>



  

 






<p class="display-messages">

 

 

</p>


<div class="main-layout">
 
   

 




<div class='page-title'>Read-write locks</div>

<div class='main-body'>
 
<br>In the great majority of database applications, the frequency of read
operations greatly exceeds the frequency of write operations. This is why
databases implement read-write locks for their records, which allow for
concurrent reading, but still demand exclusive writing. This can markedly
increase performance.
<p><em>Occasionally</em>, a class may benefit as well from a read-write lock, for
exactly the same reasons - reads are much more frequent than writes. An
example of such a class is an in-memory cache of database content. Doug
Lea's <tt>util.concurrent</tt> package contains a <tt><a href="http://g.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/ReadWriteLock.html">ReadWriteLock</a></tt>
interface and two implementations which are useful here. In addition, see
the new packages&nbsp;<tt><a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-summary.html">java.util.concurrent</tt></a>
and&nbsp;<tt><a href="http://java.sun.com/javase/6/docs/api/java/util/concurrent/locks/package-summary.html">java.util.concurrent.lock</tt></a>,
added in JDK 1.5.
<p>At runtime, most of the accesses would be handled by the cache, not
the database.
<p><b>Example</b>
<p>There are many possible behaviours for a cache. This example has simple
behaviour.
<p>It is not uncommon for some database content to change only very rarely,
or to be changed by means which lie outside your java application, and
whose liveness is not critical. For example, the list of products sold
by a company is usually not highly dynamic, and the end user never updates
it. This is a case which could be handled by this example cache, which
simply periodically reads the database content after a configured interval
of time.
<br>
<PRE>

<span class='keyword'>package</span> myapp.data;

<span class='keyword'>import</span> java.util.*;
<span class='keyword'>import</span> EDU.oswego.cs.dl.util.concurrent.ReadWriteLock;
<span class='keyword'>import</span> EDU.oswego.cs.dl.util.concurrent.WriterPreferenceReadWriteLock;

<span class='comment'>/**
* Simple, thread-safe, generic cache to hold Objects in a table, which uses
* read-write locks for maximum liveness.
*
* Use as field in DAO implementations to implement caching, if desired.
* Web.xml can control both caching refresh rates and whether or not caching
* is enabled in the first place.
*
* &lt;ul&gt;
* &lt;li&gt; the cache is thread-safe
* &lt;li&gt; the cache has read-write locks, which exploit the fact that
* most access are reads
* &lt;li&gt; if built with a refresh rate of 0 or less, then all methods are fast
* no-operations; this simplifies user code, and allows caching to be turned on
* and off at deploy time.
* &lt;li&gt; cache items are refreshed only if they have been in the cache longer
* than some configured time
* &lt;li&gt; unusual: not built to handle the case of user input affecting the
* data; only handles case of changes to the datastore eventually refreshing
* the cache. Thus, this cache is not suitable for the case in which user
* input must be reflected.
* &lt;/ul&gt;
*
* Note that this class is not visible outside the data layer.
*/</span>
<span class='keyword'>final</span> <span class='keyword'>class</span> Cache {

  <span class='comment'>/**
  * Demonstrate use.
  *
  * Note that the user does not perform any external synchronization.
  */</span>
  <span class='keyword'>public</span> <span class='keyword'>static</span> <span class='keyword'>final</span> <span class='keyword'>void</span> main( String... aArguments ) <span class='keyword'>throws</span> DataAccessException {
    <span class='comment'>//build a cache with a refresh rate of every 6 minutes
</span>    <span class='comment'>//If a negative number is passed, then caching is disabled, in the
</span>    <span class='comment'>//sense that all of the following code can be called, but it will
</span>    <span class='comment'>//simply call fast no-operations. This simplifies the caller quite a bit,
</span>    <span class='comment'>//since the caller does not need to branch according to a deployment decision.
</span>    Cache cache = <span class='keyword'>new</span> Cache(<span class='literal'>600000</span>);

    <span class='comment'>//In this example, Date objects are stored in the cache.
</span>
    <span class='comment'>//Here, we wish to retrieve a Date object having a certain integer Id.
</span>    <span class='comment'>//Instead of always going to the datastore, check the cache first: if
</span>    <span class='comment'>//it is in the cache already, return it - else fetch it from the datastore,
</span>    <span class='comment'>//store it in the cache, and then return it
</span>    Date item = <span class='keyword'>null</span>;
    Integer id = <span class='keyword'>new</span> Integer(<span class='literal'>1</span>);
    <span class='keyword'>if</span> ( cache.isPresentAndFresh(id) ) {
      item = (Date)cache.getItem( id );
    }
    <span class='keyword'>else</span> {
      <span class='comment'>//item = fetchItemFromDatastore(id);
</span>      cache.putItem( id, item );
    }
    <span class='comment'>//return item;
</span>
    <span class='comment'>//Later, the db is updated externally to this program.
</span>    <span class='comment'>//Force a refresh of all items in the cache:
</span>    cache.removeAllItems();
    <span class='comment'>//start over, as above, and each new get will quickly populate the cache
</span>  }

  <span class='comment'>/**
  * Caches are created as empty, and populated through use.
  *
  * @param aRefreshInterval and has units of milliseconds; if 0 or less, then
  * caching is disabled.
  */</span>
  Cache( <span class='keyword'>long</span> aRefreshInterval ) {
    fRefreshInterval = aRefreshInterval;
    fIsCacheEnabled = (fRefreshInterval &gt; <span class='literal'>0</span>);
    fItems = <span class='keyword'>new</span> HashMap();
    fTimePlacedIntoCache = <span class='keyword'>new</span> HashMap();
    fReadWriteLock = <span class='keyword'>new</span> WriterPreferenceReadWriteLock();
  }

  <span class='comment'>/**
  * Return true only if the corresponding item is in the cache, and has been
  * in it for no more that fRefreshInterval milliseconds; if caching is
  * disabled, then always return false.
  *
  * @param aId is non-null.
  * @exception IllegalArgumentException if a param does not comply.
  * @exception DataAccessException if any problem encountered.
  */</span>
  <span class='keyword'>public</span> <span class='keyword'>boolean</span> isPresentAndFresh( Object aId ) <span class='keyword'>throws</span> DataAccessException {
    <span class='keyword'>if</span> ( aId == <span class='keyword'>null</span> ) <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalArgumentException(<span class='literal'>"Id must not be null."</span>);
    <span class='keyword'>if</span> ( !fIsCacheEnabled ) <span class='keyword'>return</span> <span class='keyword'>false</span>;

    <span class='keyword'>boolean</span> result = <span class='keyword'>false</span>;

    startRead();
    <span class='keyword'>try</span> {
      <span class='keyword'>if</span> ( fItems.containsKey(aId) ) {
        result = (timeSpentInCache(aId) &lt; fRefreshInterval);
      }
      <span class='keyword'>else</span> {
        result = <span class='keyword'>false</span>;
      }
    }
    <span class='keyword'>finally</span> {
      finishRead();
    }
    <span class='keyword'>return</span> result;
  }

  <span class='comment'>/**
  * Retrieve an existing item from the cache.
  *
  * @param aId is non-null, and corresponds to an existing item in the cache.
  * @return a non-null Object
  * @exception IllegalArgumentException if aId is null, or if the item is not in the cache
  * @exception IllegalStateException if the item in the cache is null
  * @exception IllegalStateException if the cache is disabled
  * @exception DataAccessException if any problem encountered.
  */</span>
  Object getItem( Object aId ) <span class='keyword'>throws</span> DataAccessException {
    <span class='keyword'>if</span> ( !fIsCacheEnabled ){
      <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalStateException(<span class='literal'>"Cannot call getItem for disabled cache."</span>);
    }
    <span class='keyword'>if</span> ( aId == <span class='keyword'>null</span> ) <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalArgumentException(<span class='literal'>"Id must not be null."</span>);

    startRead();
    Object result = <span class='keyword'>null</span>;
    <span class='keyword'>try</span> {
      <span class='keyword'>if</span> ( fItems.containsKey(aId) ) {
        result = fItems.get(aId);
        <span class='keyword'>if</span> ( result == <span class='keyword'>null</span> ) {
          <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalStateException(<span class='literal'>"Stored item should not be null. Id:"</span> + aId);
        }
      }
      <span class='keyword'>else</span> {
        <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalArgumentException(<span class='literal'>"There is no item for this id:"</span> + aId);
      }
    }
    <span class='keyword'>finally</span> {
      finishRead();
    }
    <span class='keyword'>return</span> result;
  }

  <span class='comment'>/**
  * If the item is already present, then replace it; otherwise, add it.
  *
  * If the cache is disabled, do nothing.
  *
  * @param aId is non-null
  * @param aItem is non-null
  * @exception IllegalArgumentException if param does not comply
  * @exception DataAccessException if any problem encountered.
  */</span>
  <span class='keyword'>void</span> putItem( Object aId, Object aItem ) <span class='keyword'>throws</span> DataAccessException {
    <span class='keyword'>if</span> ( !fIsCacheEnabled ) <span class='keyword'>return</span>;
    <span class='keyword'>if</span> ( aId == <span class='keyword'>null</span> ) <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalArgumentException(<span class='literal'>"Id must not be null."</span>);
    <span class='keyword'>if</span> ( aItem == <span class='keyword'>null</span> ) <span class='keyword'>throw</span> <span class='keyword'>new</span> IllegalArgumentException(<span class='literal'>"Item must not be null."</span>);

    startWrite();
    <span class='keyword'>try</span> {
      fItems.put( aId, aItem );
      fTimePlacedIntoCache.put( aId, <span class='keyword'>new</span> Long(System.currentTimeMillis()) );
    }
    <span class='keyword'>finally</span> {
      finishWrite();
    }
  }

  <span class='comment'>/**
  * Start from beginning, and remove all items from the cache; if cache is
  * disabled, do nothing.
  *
  * Forces a re-population of all items into the cache.
  * @exception DataAccessException if any problem encountered.
  */</span>
  <span class='keyword'>void</span> removeAllItems() <span class='keyword'>throws</span> DataAccessException {
    <span class='keyword'>if</span> ( !fIsCacheEnabled ) <span class='keyword'>return</span>;

    startWrite();
    <span class='keyword'>try</span> {
      fItems.clear();
      fTimePlacedIntoCache.clear();
    }
    <span class='keyword'>finally</span> {
      finishWrite();
    }
  }

  <span class='comment'>//PRIVATE ////
</span>
  <span class='comment'>/**
  * After being in the cache more than this number of milliseconds, then an item
  * is considered to be no longer fresh.
  *
  * If the value is 0 or less, then all cache methods are fast no-operations.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> <span class='keyword'>long</span> fRefreshInterval;

  <span class='comment'>/**
  * Set to false only if fRefreshInterval is 0 or less.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> <span class='keyword'>boolean</span> fIsCacheEnabled;

  <span class='comment'>/**
  * Stores all items placed in the cache.
  *
  * KEY - Id Object&lt;br&gt;
  * VALUE - Object
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> Map fItems;

  <span class='comment'>/**
  * This table stores the time of entry of all items into the cache, and forms
  * a table which is parallel to fItems.
  *
  * KEY - same as Id used in fItems&lt;br&gt;
  * VALUE - (Long object) the time of entry expressed in milliseconds
  * since the Java epoch.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> Map fTimePlacedIntoCache;

  <span class='comment'>/**
  * Read-write lock allows maximal concurrency, since readers can share access;
  * only writers need sole access.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>final</span> ReadWriteLock fReadWriteLock;

  <span class='comment'>/**
  * The number of milliseconds the item has been in the cache.
  *
  * It is CRITICAL that this method be called by a public method which
  * has already acquired a read lock.
  *
  * @param aId is non-null and exists in the cache.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>long</span> timeSpentInCache( Object aId ) <span class='keyword'>throws</span> DataAccessException {
    <span class='keyword'>final</span> Long timePlacedInCache = (Long)fTimePlacedIntoCache.get(aId);
    <span class='keyword'>return</span> System.currentTimeMillis() - timePlacedInCache.longValue();
  }

  <span class='comment'>/**
  * Blocks of code in which the contents of fItems and fTimePlacedIntoCache
  * are examined in any way must be surrounded by calls to &lt;code&gt;startRead&lt;/code&gt;
  * and &lt;code&gt;finishRead&lt;/code&gt;. See documentation for ReadWriteLock.
  *
  * Translates the InterruptedException into a generic DataAccessException, to
  * protect the higher layers from implementation details.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> startRead() <span class='keyword'>throws</span> DataAccessException {
    <span class='keyword'>try</span> {
      fReadWriteLock.readLock().acquire();
    }
    <span class='keyword'>catch</span> (InterruptedException ex){
      <span class='keyword'>throw</span> <span class='keyword'>new</span> DataAccessException(<span class='literal'>"Custom cache cannot acquire read lock."</span>, ex);
    }
  }

  <span class='keyword'>private</span> <span class='keyword'>void</span> finishRead() {
    fReadWriteLock.readLock().release();
  }

  <span class='comment'>/**
  * Blocks of code in which the contents of fItems and fTimePlacedIntoCache
  * are changed in any way must be surrounded by calls to &lt;code&gt;startWrite&lt;/code&gt; and
  * &lt;code&gt;finishWrite&lt;/code&gt;. See documentation for ReadWriteLock.
  *
  * Translates the InterruptedException into a generic DataAccessException, to
  * protect the higher layers from implementation details.
  */</span>
  <span class='keyword'>private</span> <span class='keyword'>void</span> startWrite() <span class='keyword'>throws</span> DataAccessException {
    <span class='keyword'>try</span> {
      fReadWriteLock.writeLock().acquire();
    }
    <span class='keyword'>catch</span> (InterruptedException ex){
      <span class='keyword'>throw</span> <span class='keyword'>new</span> DataAccessException(<span class='literal'>"Custom cache cannot acquire write lock."</span>, ex);
    }
  }

  <span class='keyword'>private</span> <span class='keyword'>void</span> finishWrite() {
    fReadWriteLock.writeLock().release();
  }
} 
</PRE>
<br>
<br>
<br>

</div>




<div class='topic-section'>See Also :</div>
<div class='main-body'>
 
  
  <a href='TopicAction2b10-2.html?Id=61'>Remember the basics of database design</a> <br>
 
  
  <a href='TopicAction564b-2.html?Id=66'>Data access objects</a> <br>
 
</div>


<div class='topic-section'>Would you use this technique?</div>
<div class='main-body'>
  
  <form action="http://www.javapractices.com/vote/AddVoteAction.do" method='post'>
    Yes<input type='radio' name='Choice' value='Y' >
    &nbsp;&nbsp;No<input type='radio' name='Choice' value='N'>
    &nbsp;&nbsp;Undecided<input type='radio' name='Choice' value="?" >
    &nbsp;&nbsp;<input type=submit value="Vote" >
    <input type='hidden' name='Operation' value='Apply'>
    <input type='hidden' name='TopicId' value='118'>
  </form>
</div>

<div style='height:10.0em;'></div>

 
 
</div>

  

 





<div align='center' class='legalese'>  
&copy; 2011 Hirondelle Systems |
<a href='../source/SourceAction-2.html'><b>Source Code</b></a><IMG class='no-margin' SRC="../images/goldstar.gif" ALT=""> |
<a href="mailto:webmaster@javapractices.com">Contact</a> |
<a href="http://creativecommons.org/licenses/by-nc-sa/1.0/">License</a> |
<a href='../apps/cjp.rss'>RSS</a>
<!-- ukey="2AC36CD2" -->
<!-- ckey="16DF3D87" -->
<br>

 Individual code snippets can be used under this <a href='../LICENSE.txt'>BSD license</a> - Last updated on June 6, 2010.<br>
 Over 150,000 unique IPs last month - <span title='Java Practices 2.6.5, Mon May 16 00:00:00 EDT 2011'>Built with</span> <a href='http://www.web4j.com/'>WEB4J</a>.<br>
 - In Memoriam : Bill Dirani -
</div>

<script src="../../www.google-analytics.com/urchin.js" type="text/javascript">
</script>
<script type="text/javascript">
_uacct = "UA-2633428-1";
urchinTracker();
</script>



</body>

<!-- Mirrored from www.javapractices.com/topic/TopicAction.do?Id=118 by HTTrack Website Copier/3.x [XR&CO'2010], Sun, 12 Jun 2011 17:28:07 GMT -->
<!-- Added by HTTrack --><meta http-equiv="content-type" content="text/html;charset=UTF-8"><!-- /Added by HTTrack -->
</html>
